﻿using GTMC.Enum;
using GTMC.Model;
using LEDDisplay.SDK;
using Leeder.Logger;
using Leeder.Util;
using SPSAndong.Business;
using SPSAndong.Comm;
using SPSAndong.Enum;
using SPSAndong.Model;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TitaniumAS.Opc.Client.Common;
using TitaniumAS.Opc.Client.Da;

namespace SPSAndong.Handle
{
    public class SPSAndongHandler
    {
        /// <summary>
        /// 日志
        /// </summary>
        LogOperate LogObj => Log.GetLogger(this.GetType());
        /// <summary>
        /// 按钮呼叫超时线程
        /// </summary>
        CancellationTokenSource cancel_buttonStateTimeOut;
        /// <summary>
        /// LED屏显示
        /// </summary>
        CancellationTokenSource cancel_ledShow;
        /// <summary>
        /// 按钮呼叫线程
        /// </summary>
        CancellationTokenSource cancel_buttonState;
        /// <summary>
        /// 按钮状态改变事件
        /// </summary>
        public event Action OnButtonStateChanged;
        /// <summary>
        /// 设备状态改变事件
        /// </summary>
        public event Action OnDeviceStateChanged;
        /// <summary>
        /// 加班时间改变事件
        /// </summary>
        public event Action OnOuttimeChanged;

        /// <summary>
        /// 安东屏设备状态改变事件
        /// </summary>
        public event Action OnAndonDeviceStateChanged;

        /// <summary>
        /// 主要设备监控线程
        /// </summary>
        public CancellationTokenSource cancel_mainequip;
        /// <summary>
        /// 定义ups服务变量
        /// </summary>
        private UpsHandle upsServer;
        /// <summary>
        /// 安东屏设备监控线程
        /// </summary>
        public CancellationTokenSource cancel_andonquip;
        /// <summary>
        /// 程序编号
        /// </summary>
        public ProgramCode ProgramCode { get; set; } = ProgramCode.Program01;

        TriaConnectBLL triaConnectBLL = new TriaConnectBLL();
        TriaGroupBLL triaGroupBLL = new TriaGroupBLL();
        TriaItemBLL triaItemBLL = new TriaItemBLL();
        LedShowBLL ledShowBLL = new LedShowBLL();
        ProjectSetBLL projectSetBLL = new ProjectSetBLL();
        SystemSetupBLL systemSetupBLL = new SystemSetupBLL();
        CallDetailsBLL callDetailsBLL = new CallDetailsBLL();
        ProductionBLL productionBLL = new ProductionBLL();
        MusicSetBLL musicSetBLL = new MusicSetBLL();
        DeviceExceptionMonitorBLL deviceExceptionMonitorBLL = new DeviceExceptionMonitorBLL();
        AutoResetEvent atresetEvent = new AutoResetEvent(false);

        private string[] AreaNames;

        /// <summary>
        /// 单例模式
        /// </summary>
        private static SPSAndongHandler instance = new SPSAndongHandler();
        public static SPSAndongHandler getInstance()
        {
            if (instance == null)
                instance = new SPSAndongHandler();
            return instance;
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        private SPSAndongHandler()
        {

        }
        /// <summary>
        /// 初始化
        /// </summary>
        public void Init()
        {
            InitData();
            InitToyopuc();
            InitTask();
        }

        private void InitData()
        {
            //初始化服务
            var sysSet = systemSetupBLL.GetModel();
            GolbalVariable.ProjectBag = new ConcurrentBag<ProjectSetModel>(new ProjectSetBLL().GetModelAll());
            GolbalVariable.ProjectBag.ForEach(x => x.State = 0); //初始化状态为0
            GolbalVariable.AppSet = new AppSetModel();
            GolbalVariable.AppSet.AndongShowRefresh = 1;
            GolbalVariable.AppSet.ExceptionCall = sysSet.ExceptionCall;
            GolbalVariable.AppSet.ButtonWorkDispatchTimeSpan = 1000;
            GolbalVariable.AppSet.VoiceListenPort = 23;
            GolbalVariable.VoiceAreaMusicList = new MusicSetBLL().GetMusicBroadcastSet();
            GolbalVariable.SystemDeviceBag = new ConcurrentBag<SystemDeviceModel>(new SystemDeviceBLL().GetModelAll());
            GolbalVariable.VoiceSever = new VoiceHelper();
            GolbalVariable.VoiceSever.InitSocket();

            AreaNames = GolbalVariable.ProjectBag.Select(x => x.AreaName).Distinct().ToArray();
        }

        /// <summary>
        /// 初始化丰田工机
        /// </summary>
        public void InitToyopuc()
        {
            GolbalVariable.TriaConnectList = triaConnectBLL.GetModelList();
            GolbalVariable.TriaGroupList = triaGroupBLL.GetModelList();
            GolbalVariable.TriaItemList = triaItemBLL.GetModelList();
            GolbalVariable.PlcTriaList = new List<TriaModel>();

            foreach (var conn in GolbalVariable.TriaConnectList)
            {
                TriaModel triaModel = new TriaModel();
                GolbalVariable.PlcTriaList.Add(triaModel);
                triaModel.ToyopucHandler = Leeder.GTMC.Communi.ToyopucInstance.GetInstance(conn.Ip, conn.Port);
                triaModel.Ip = conn.Ip;
                triaModel.Port = conn.Port;
                triaModel.AccessMode = conn.AccessMode;
                triaModel.ProgramCode = (ProgramCode)conn.ProgramCode;

                triaModel.TriaAddress = new IndirectAddress();
                triaModel.TriaAddress.Identifier = conn.Identifier;
                triaModel.TriaAddress.Address = (uint)conn.Address;
                triaModel.AddressLength = (short)conn.AddressLength;
                triaModel.TriaGroups = new List<TriaGroup>();
                for (uint i = triaModel.TriaAddress.Address; i < (triaModel.TriaAddress.Address + triaModel.AddressLength); i++)
                {
                    var groupId = triaModel.TriaAddress.Identifier + i.ToString("X");
                    var group = GolbalVariable.TriaGroupList.Where(s => s.GroupAddress == groupId && s.TriaConnectID == conn.ID).FirstOrDefault();
                    if (group == null)
                        continue;
                    TriaGroup triaGroup = new TriaGroup
                    {
                        GroupId = groupId,
                        GroupDataType = group.GroupType == 0 ? TriaDataType.Booleans : TriaDataType.Integer,
                        Items = new List<TriaItem>()
                    };
                    triaModel.TriaGroups.Add(triaGroup);
                    switch (triaGroup.GroupDataType)
                    {
                        case TriaDataType.Integer:
                            triaGroup.Items.Add(new TriaItem
                            {
                                ItemId = triaGroup.GroupId + "_0"
                            });
                            break;
                        case TriaDataType.Booleans:
                            for (int j = 0; j < 16; j++)
                            {
                                triaGroup.Items.Add(new TriaItem
                                {
                                    ItemId = triaGroup.GroupId + "_" + j.ToString("X")
                                });
                            }
                            break;
                    }
                }
            }
        }

        /// <summary>
        /// 初始化线程
        /// </summary>
        private void InitTask()
        {
            //呼叫超时监控线程(判断是否有呼叫超时)
            cancel_buttonStateTimeOut = new CancellationTokenSource();
            Task.Factory.StartNew(() =>
            {
                ButtonStateTimeOut();
            }, cancel_buttonStateTimeOut.Token);

            //呼叫超时监控线程(判断是否有呼叫超时)
            cancel_ledShow = new CancellationTokenSource();
            Task.Factory.StartNew(() =>
            {
                LedShow();
            }, cancel_ledShow.Token);

            //丰田工机数据监控线程(读取数据，判断是否有按钮按下)
            cancel_buttonState = new CancellationTokenSource();
            Task.Factory.StartNew(() =>
            {
                ButtonState();
            }, cancel_buttonState.Token);
            TriaMonitor();
            VoiceMonitor();
            //主要设备监控线程
            if (GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "IP").Any())
            {
                cancel_mainequip = new CancellationTokenSource();
                LogObj.Info("开启主要设备监控线程启动");
                Task.Factory.StartNew(() =>
                {
                    BindMainPower();
                }, cancel_mainequip.Token);
            }

            //创建ups监控对象的实例
            upsServer = new UpsHandle();
            //接收数据事件
            upsServer.Received += UpsServer_Received;
            //初始化com口
            LogObj.Info("ups监控线程开启");
            upsServer.InitCom();

            //安东屏设备监控线程
            if (GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "LED").ToList().Any())
            {
                cancel_andonquip = new CancellationTokenSource();
                LogObj.Info("开启安东屏设备监控线程");
                Task.Factory.StartNew(() =>
                {
                    BindAndon();
                }, cancel_andonquip.Token);
            }

        }

        /// <summary>
        /// 生产台数
        /// </summary>
        /// <param name="signalMark">信号源</param>
        /// <param name="state">数量</param>
        private void ProductionQty(string signalMark, string qty)
        {
            LogObj.Info("生产信息变化");
            var production = productionBLL.GetModel(signalMark);
            if (production != null)
            {
                production.Value = production.DataType + qty;
                production.Value = production.DataType + qty;
                productionBLL.Update(production);
            }
            else
            {
                production = new ProductionModel();
                production.SignalMark = signalMark;
                production.Value = production.DataType + qty;
                production.Value = production.DataType + qty;
                productionBLL.Add(production);
            }
        }

        /// <summary>
        /// 设备监控
        /// </summary>
        /// <param name="signalMark">信号源</param>
        /// <param name="state">状态值</param>
        private void DeviceMonitor(string signalMark, int state)
        {
            var plans = GolbalVariable.SystemDeviceBag.Where(x => x.SignalMark == signalMark && x.DataSource == "PLC").ToList();
            if (plans != null)
                plans.ForEach(x =>
                {
                    x.Status = state;
                });
        }

        /// <summary>
        /// 按钮信号处理
        /// </summary>
        /// <param name="signalMark">信号源</param>
        /// <param name="state">状态</param>
        private void MonitorButtonStateChange(string signalMark, int state)
        {
            //查找OPC触发对应的按钮
            var plans = GolbalVariable.ProjectBag.Where(x => x.SignalMark == signalMark).ToList();
            if (plans != null)
            {
                plans.ForEach(x =>
                {
                    if (x.State != state)
                    {
                        if (x.State == 0)
                        {
                            //正常状态下按下按钮该部品状态变为呼叫状态
                            x.ActualStartTime = DateTime.Now; //按钮状态为,1，开始呼叫，记录实际开始时间                                                            
                            GolbalVariable.VoiceSever.ExecVoiceCommand(x.AreaType, true);//将讲呼岗位加入音乐播放队列
                                                                                         //写入LED显示数据库表：呼叫异常显示：0：正常(完成)、1：异常呼叫、2：呼叫超时
                            x.State = 1;
                            CallDetailsModel callDetailsModel = new CallDetailsModel();
                            callDetailsModel.AreaName = x.AreaName;
                            callDetailsModel.AreaType = x.AreaType;
                            callDetailsModel.ProjectName = x.WorkName;
                            callDetailsModel.CreateTime = DateTime.Now;
                            callDetailsBLL.Add(callDetailsModel);

                            //保存呼叫记录
                            LogObj.Info(x.AreaName + "->" + x.WorkName + ":异常呼叫");
                        }
                        else
                        {
                            //呼叫或超时呼叫状态下按下按钮该部品状态变为正常状态
                            x.ActualEndTime = DateTime.Now; //按钮状态为,0，结束呼叫，记录实际结束时间                                                             
                            //GolbalVariable.VoiceSever.ExecVoiceCommand(x.AreaType, true);//将讲呼岗位加入音乐播放队列
                                                                                         //写入LED显示数据库表：呼叫异常显示：0：正常(完成)、1：异常呼叫、2：呼叫超时
                            x.State = 0;
                            if (GolbalVariable.ProjectBag.Count(s => s.State != 0 && s.AreaType == x.AreaType) == 0)
                                GolbalVariable.VoiceSever.ExecVoiceCommand(x.AreaType, false, ""); //将结束音乐播放，加入队列

                            var callDetailsModel = callDetailsBLL.GetModel(x.WorkName);
                            if(callDetailsModel != null)
                            {
                                callDetailsModel.ResponseTime = DateTime.Now;
                                callDetailsBLL.Update(callDetailsModel);
                            }
                            //保存呼叫记录
                            LogObj.Info(x.AreaName + "->" + x.WorkName + ":呼叫完成");
                        }
                    }
                });
                projectSetBLL.UpdateList(plans);
            }
        }

        /// <summary>
        /// 停止全部声音
        /// </summary>
        public void StopVoiceAll()
        {
            var list = musicSetBLL.GetModelAll();
            foreach (var item in list)
            {
                GolbalVariable.VoiceSever.ExecVoiceCommand(item.AreaType, false, ""); //将结束音乐播放，加入队列
            }
        }

        /// <summary>
        /// 设备状态改变
        /// </summary>
        /// <param name="signalMark">信号源</param>
        /// <param name="state">状态</param>
        private void DeviceStateChange(string signalMark, int state)
        {
            //查找OPC触发对应的设备
            var plans = GolbalVariable.SystemDeviceBag.Where(x => x.SignalMark == signalMark).ToList();
            if (plans != null)
                plans.ForEach(x =>
                {
                    x.Status = state;
                });
        }

        /// <summary>
        /// 丰田工机通讯监控
        /// </summary>
        public void TriaMonitor()
        {
            Thread thread = new Thread(() =>
            {
                while (true)
                {
                    bool re = atresetEvent.WaitOne(60000);//等60秒，60秒没有信号则取消旧线程重新创建读取线程
                    if (re)
                    {

                    }
                    else
                    {
                        LogObj.Error("丰田工机通讯异常，无法读取数据！");
                        //丰田工机数据监控线程(读取数据，判断是否有按钮按下)
                        cancel_buttonState.Cancel();
                        Thread.Sleep(300);
                        cancel_buttonState = new CancellationTokenSource();
                        Task.Factory.StartNew(() =>
                        {
                            InitToyopuc();
                            ButtonState();
                        }, cancel_buttonState.Token);
                    }
                }
            }
            );
            thread.IsBackground = true;
            thread.Start();
        }

        bool IsVoiceReConnect = false;
        /// <summary>
        /// 喇叭通讯监控
        /// </summary>
        public void VoiceMonitor()
        {
            Thread thread = new Thread(() =>
            {
                while (true)
                {
                    try
                    {
                        int unlinQty = 0;
                        int onlinQty = 0;
                        if(IsVoiceReConnect)
                        {
                            foreach (var item in GolbalVariable.VoiceAreaMusicList)
                            {
                                var result = PublicHelper.PingIp(item.IPAddress);
                                if (!result)
                                {
                                    onlinQty++;
                                    if (onlinQty >= GolbalVariable.VoiceAreaMusicList.Count / 2)
                                    {
                                        //超过一半PING通时，全部控制器重连
                                        IsVoiceReConnect = false;
                                        LogObj.Info("声音控制器重连！");
                                        GolbalVariable.VoiceSever.CloseAllVoice();
                                        GolbalVariable.VoiceSever.StopVoiceServer();
                                        Thread.Sleep(10000);

                                        GolbalVariable.VoiceSever = new VoiceHelper();
                                        GolbalVariable.VoiceSever.InitSocket();
                                        break;
                                    }
                                }
                            }
                        }else
                        {
                            foreach (var item in GolbalVariable.VoiceAreaMusicList)
                            {
                                var result = PublicHelper.PingIp(item.IPAddress);
                                if (!result)
                                {
                                    unlinQty++;
                                    if (unlinQty >= GolbalVariable.VoiceAreaMusicList.Count / 2)
                                    {
                                        //超过一半PING不通时，全部控制器重连
                                        IsVoiceReConnect = true;
                                        LogObj.Info("声音控制器网络异常！");
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        LogObj.Error($"声音控制器监控器监控异常:{ex.ToString()}");
                    }
                    Thread.Sleep(30000);
                }
            }
            );
            thread.IsBackground = true;
            thread.Start();
        }

        /// <summary>
        /// 按钮呼叫线程
        /// </summary>
        private async void ButtonState()
        {
            LogObj.Info("呼叫监控线程开始");
            while (!this.cancel_buttonState.Token.IsCancellationRequested)
            {
                //遍历所有连接
                foreach (var plcTria in GolbalVariable.PlcTriaList)
                {
                    try
                    {
                        await Task.Delay(20);
                        //如果连接
                        if (plcTria.AccessMode == "writeOnly")
                            continue;
                        var toyopucHandler = plcTria.ToyopucHandler;
                        byte[] result = null;
                        try
                        {
                            //await Task.Delay(20000);//测试代码
                            //查询当前按钮的状态
                            result = toyopucHandler.ReadExtString(plcTria.ProgramCode, plcTria.TriaAddress, plcTria.AddressLength);//PublicHelper.ReadPlcDataTest(plcTria);//测试代码
                            if (result.Length > 0)
                                atresetEvent.Set();
                        }
                        catch (Exception ex)
                        {
                            LogObj.Error("PLC读取失败：" + ex.Message);
                            continue;
                        }
                        //将读到的字节转换成二进制字符串
                        string resultStr = ByteToStr(result);
                        //比较当前读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理
                        if (plcTria.DataStr != resultStr)
                        {
                            plcTria.DataByte = result;
                            plcTria.DataStr = resultStr;
                            int packetIndex = 0;
                            //循环判断每一组数据
                            for (uint i = plcTria.TriaAddress.Address; i < (plcTria.TriaAddress.Address + plcTria.AddressLength); i++)
                            {
                                try
                                {
                                    //根据组ID获取该组
                                    var triaGroup = plcTria.TriaGroups.Where(s => s.GroupId == plcTria.TriaAddress.Identifier + i.ToString("X")).FirstOrDefault();

                                    if (triaGroup == null)
                                        continue;
                                    //截取该组数据(转换后的二进制字符串)
                                    var groupStates = resultStr.FindIndexString(ref packetIndex, 16);
                                    //比较该组当前读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理
                                    if (triaGroup.GroupStrs != groupStates)
                                    {
                                        triaGroup.Value = BitConverter.ToInt16(result, (int)((i - plcTria.TriaAddress.Address) * 2));
                                        LogObj.Debug($"{triaGroup.GroupId}:{triaGroup.Value.ToString()}");
                                        triaGroup.GroupStrs = groupStates;
                                        var group = GolbalVariable.TriaGroupList.Where(s => s.GroupAddress == plcTria.TriaAddress.Identifier + i.ToString("X")).FirstOrDefault();
                                        var items = GolbalVariable.TriaItemList.Where(s => s.GroupID == group.ID).ToList();
                                        //根据组名分别调用不同的逻辑方法
                                        switch (triaGroup.GroupDataType)
                                        {
                                            case TriaDataType.Integer:
                                                //比较当前项读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理          
                                                if (triaGroup.Items[0].Value == null || (triaGroup.Items[0].Value != triaGroup.Value))
                                                {
                                                    //将读到的二进制数转成十进制数并更新到数据模型中
                                                    triaGroup.Items[0].Value = triaGroup.Value;
                                                    switch (group.SignalMark)
                                                    {
                                                        case "OUTTIME":
                                                            LogObj.Info($"加班时间变化");
                                                            LogObj.Info($"加班时间-->{triaGroup.Items[0].ItemId}:{triaGroup.Items[0].Value.ToString()}");
                                                            GolbalVariable.Overtime = triaGroup.Items[0].Value.ToInt();
                                                            ledShowBLL.UpdateOverTime(GolbalVariable.Overtime); 
                                                            OnOuttimeChanged?.BeginInvoke(null, null);
                                                            break;
                                                        case "PRODUCTION":
                                                            LogObj.Info($"生产数量变化");
                                                            LogObj.Info($"生产数量-->{triaGroup.Items[0].ItemId}:{triaGroup.Items[0].Value.ToString()}");
                                                            ProductionQty(triaGroup.Items[0].ItemId, triaGroup.Items[0].Value.ToString());
                                                            break;
                                                    }
                                                }
                                                break;
                                            case TriaDataType.Booleans:
                                                for (int j = 0; j < 16; j++)
                                                {
                                                    //比较当前项读到数据是否与上一次记录数据一样，一样则不做处理，不一样则进行下一步处理
                                                    if (triaGroup.Items[j].Value == null || ((int)triaGroup.Items[j].Value != int.Parse(groupStates[j].ToString())))
                                                    {
                                                        var item = items.Where(s => s.ItemAddress == triaGroup.Items[j].ItemId).FirstOrDefault();
                                                        //将读到的二进制数转成单个0或1并更新到数据模型中
                                                        triaGroup.Items[j].Value = int.Parse(groupStates[j].ToString());
                                                        switch (group.SignalMark)
                                                        {
                                                            case "BUTTON":
                                                                MonitorButtonStateChange(items[j].SignalMark, (int)triaGroup.Items[j].Value);
                                                                break;
                                                            case "SBJK":
                                                                DeviceMonitor(items[j].SignalMark, (int)triaGroup.Items[j].Value);
                                                                break;
                                                        }
                                                    }
                                                }
                                                break;
                                        }
                                    }
                                }
                                catch (Exception ex)
                                {
                                    LogObj.Error(ex.Message);
                                    continue;
                                }
                            }
                        }
                    }
                    catch(Exception exce)
                    {
                        LogObj.Error(exce.Message);
                    }
                }
                //休眠
                await Task.Delay(100);
            }
            LogObj.Info("呼叫监控线程结束");
        }

        /// <summary>
        /// 异常呼叫状态超时线程
        /// </summary>
        private async void ButtonStateTimeOut()
        {
            while (!this.cancel_buttonStateTimeOut.Token.IsCancellationRequested)
            {
                //查询当前正在呼叫的按钮
                var datas = GolbalVariable.ProjectBag.Where(x => x.State == 1).ToList();
                if (datas.Count > 0)
                {
                    foreach (var item in datas)
                    {
                        if (item.ActualStartTime != null)
                        {
                            if ((DateTime.Now - item.ActualStartTime.Value).TotalSeconds >= GolbalVariable.AppSet.ExceptionCall)
                            {
                                //当异常呼叫的时间超过配置时间，默认30秒
                                item.UpdateTime = DateTime.Now;
                                item.IsCallOutTime = true;
                                item.State = 2;
                                projectSetBLL.Update(item);
                                LogObj.Info(item.AreaName + "->" + item.WorkName + ":呼叫超时");
                            }
                        }
                    }
                }
                //休眠
                await Task.Delay(GolbalVariable.AppSet.ButtonWorkDispatchTimeSpan);
            }
        }

        private bool plcStatus = true;
        private bool lipStatus = true;
        /// <summary>
        /// 绑定硬件设备
        /// </summary>
        private async void BindMainPower()
        {
            var plc = GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "IP" && x.DeviceName == "PLC").FirstOrDefault();
            var lip = GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "IP" && x.DeviceName == "LIP").FirstOrDefault();
            string plcIp = plc.DeviceIpAddress;
            string lipIp = lip.DeviceIpAddress;
            while (true)
            {
                if (cancel_mainequip.Token.IsCancellationRequested)
                {
                    cancel_mainequip.Dispose();
                    break;
                }
                plcStatus = PublicHelper.PingIp(plcIp);
                lipStatus = PublicHelper.PingIp(lipIp);
                if (!plcStatus && GolbalVariable.PlcState != false)
                {
                    LogObj.Error("PLC连接异常");
                    GolbalVariable.PlcState = false;
                    var model = new DeviceExceptionMonitorModel()
                    {
                        DeviceId = plc.ID.ToString(),
                        DeviceName = plc.DeviceName,
                        DeviceIP = plc.DeviceIpAddress,
                        DeviceType = Enum.DeviceType.PLC,
                        ExceptionDescription = "PLC连接异常",
                        ExceptionDate = DateTime.Now,
                    };
                    AddRecord(model);
                }
                else
                { GolbalVariable.PlcState = plcStatus; }
                if (!lipStatus && GolbalVariable.LipState != false)
                {
                    LogObj.Error("LIP连接异常");
                    GolbalVariable.LipState = false;
                    var model = new DeviceExceptionMonitorModel()
                    {
                        DeviceId = lip.ID.ToString(),
                        DeviceName = lip.DeviceName,
                        DeviceIP = lip.DeviceIpAddress,
                        DeviceType = Enum.DeviceType.LIP,
                        ExceptionDescription = "LIP连接异常",
                        ExceptionDate = DateTime.Now,
                    };
                    AddRecord(model);
                }
                else
                { GolbalVariable.LipState = lipStatus; }
                OnDeviceStateChanged?.BeginInvoke(null, null);
                await Task.Delay(5000);
            }
        }

        /// <summary>
        /// 绑定安东屏设备
        /// </summary>
        private async void BindAndon()
        {
            while (true)
            {
                if (cancel_mainequip.Token.IsCancellationRequested)
                {
                    cancel_mainequip.Dispose();
                    break;
                }
                var status = false;
                foreach (var andon in GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "LED").ToList())
                {
                    if (string.IsNullOrEmpty(andon.DeviceIpAddress))
                        continue;
                    status = PublicHelper.PingIp(andon.DeviceIpAddress);
                    //现在ping不通，上一次ping得通，说明断开连接了，记录异常履历
                    if (!status && andon.Status != 0)
                    {
                        LogObj.Error("安东屏连接异常");
                        andon.Status = status?1:0;
                        var model = new DeviceExceptionMonitorModel()
                        {
                            DeviceId = andon.ID.ToString(),
                            DeviceName = andon.DeviceName,
                            DeviceIP = andon.DeviceIpAddress,
                            DeviceType = Enum.DeviceType.Andon,
                            ExceptionDescription = "安东屏连接异常",
                            ExceptionDate = DateTime.Now,
                        };
                        AddRecord(model);
                    }
                    else
                    {
                        andon.Status = status ? 1 : 0;
                    }
                }

                OnAndonDeviceStateChanged?.BeginInvoke(null, null);
                await Task.Delay(5000);
            }
        }

        /// <summary>
        /// 异步新增一个异常履历
        /// </summary>
        /// <param name="model"></param>
        private void AddRecord(DeviceExceptionMonitorModel model)
        {
            Thread thread = new Thread(() =>
            {
                deviceExceptionMonitorBLL.AddOne(model);
            }
           );
            thread.IsBackground = true;
            thread.Start();
        }


        /// <summary>
        /// UPS接收数据事件
        /// </summary>
        /// <param name="data"></param>
        private void UpsServer_Received(UpsModel data)
        {
            try
            {
                LogObj.Debug(DateTime.Now + ": 输入电压:" + data.InputVoltage + "  输出电压:" + data.OutputVoltage + "  负载:" + data.Load + "  温度:" + data.Temperature + "  负载:" + data.Load);
                //把UPS当前状态更新到缓存中（用于界面状态的更新）
                GolbalVariable.UpsStatus = data;
                //数据处理
                UpsBusiness.OnExceptionEvent += UpsEvent;
                UpsBusiness.CheckUps(data);
                OnDeviceStateChanged?.BeginInvoke(null, null);
            }catch (Exception ex)
            {
                LogObj.Error(ex.ToString());
            }
        }

        public void UpsEvent(string mesg)
        {
            LogObj.Error(mesg);
        }

        /// <summary>
        /// 字节数组转二进制字符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private string ByteToStr(byte[] bytes)
        {
            string strResult = "";
            for (int i = 0; i < bytes.Length; i++)
            {
                string strTemp = Convert.ToString(bytes[i], 2);
                strTemp = strTemp.Insert(0, new string('0', 8 - strTemp.Length));
                //倒叙
                strTemp = ReverseD(strTemp);
                strResult += strTemp;
            }
            return strResult;
        }

        /// <summary>
        /// 二进制字符串转字节数组
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private byte[] StrToByte(string str)
        {
            byte[] bytes = new byte[str.Length / 8];
            for (int i = 0; i < bytes.Length; i++)
            {
                bytes[i] = Convert.ToByte(str.Substring(i * 8, 8), 2);
            }
            return bytes;
        }

        /// <summary>
        /// 字符串倒叙
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public string ReverseD(string text)
        {
            return new string(text.ToCharArray().Reverse().ToArray());
        }

        /// <summary>
        /// 
        /// </summary>
        private async void LedShow()
        {
            while (!this.cancel_ledShow.Token.IsCancellationRequested)
            {
                //查询当前正在呼叫的按钮
                var datas = GolbalVariable.ProjectBag.Where(x => x.State == 1 || x.State == 2).ToList();
                if (datas.Count > 0)
                {
                    var names = datas.Select(x => x.AreaName).Distinct().ToArray();

                    foreach (var item in AreaNames)
                    {
                        if (names.Contains(item))
                        {
                            //如果有呼叫，LED屏显示呼叫工程
                            var callList = datas.Where(x => x.AreaName == item).ToList();
                            LedShowModel ledShow = ledShowBLL.GetModel(item);
                            bool isAdd = false;
                            if (ledShow == null)
                            {
                                ledShow = new LedShowModel();
                                isAdd = true;
                            }
                            ledShow.AreaName = item;
                            ledShow.Line1 = "";
                            ledShow.Line2 = "";
                            ledShow.Line3 = "";
                            ledShow.Line4 = "";
                            ledShow.Line5 = "";
                            ledShow.Line1Status = 0;
                            ledShow.Line2Status = 0;
                            ledShow.Line3Status = 0;
                            ledShow.Line4Status = 0;
                            ledShow.Line5Status = 0;

                            for (int i = 0; i < callList.Count; i++)
                            {
                                if (i == 0)
                                {
                                    ledShow.Line1 = callList[i].WorkName;
                                    ledShow.Line1Status = callList[i].State;
                                }
                                else if(i== 1)
                                {
                                    ledShow.Line2 = callList[i].WorkName;
                                    ledShow.Line2Status = callList[i].State;
                                }
                                else if (i == 2)
                                {
                                    ledShow.Line3 = callList[i].WorkName;
                                    ledShow.Line3Status = callList[i].State;
                                }
                                else if (i == 3)
                                {
                                    ledShow.Line4 = callList[i].WorkName;
                                    ledShow.Line4Status = callList[i].State;
                                }
                                else if (i == 4)
                                {
                                    ledShow.Line5 = callList[i].WorkName;
                                    ledShow.Line5Status = callList[i].State;
                                }
                            }

                            if (isAdd)
                                ledShowBLL.Add(ledShow);
                            else
                                ledShowBLL.Update(ledShow);
                        }
                        else
                        {
                            //如果没有呼叫，LED屏显示统计数据
                            var totality = GetCallTotality(item);    //查询区域呼叫总数
                            var list = GetCallList(item);
                            var projectNames = list.Select(x => x.ProjectName).Distinct().ToArray();
                            var otherQty = 0;
                            List<SortModel> sorts = new List<SortModel>();
                            foreach (var name in projectNames)
                            {
                                var count = list.Where(s => s.ProjectName == name).Count();
                                SortModel sortModel = new SortModel();
                                sortModel.ProjectName = name;
                                sortModel.Qty = count;
                                sorts.Add(sortModel);

                            }
                            sorts = sorts.OrderByDescending(a => a.Qty).ToList();
                            LedShowModel ledShow = ledShowBLL.GetModel(item);
                            bool isAdd = false;
                            if (ledShow == null)
                            {
                                ledShow = new LedShowModel();
                                isAdd = true;
                            }
                            ledShow.AreaName = item;
                            
                            ledShow.Line1 = TxtFormatting("呼叫总数", totality.ToString());
                            ledShow.Line2 = sorts.Count >= 1 ? TxtFormatting(sorts[0].ProjectName, sorts[0].Qty.ToString()) : "";
                            ledShow.Line3 = sorts.Count >= 2 ? TxtFormatting(sorts[1].ProjectName, sorts[1].Qty.ToString()) : "";
                            ledShow.Line4 = sorts.Count >= 3 ? TxtFormatting(sorts[2].ProjectName, sorts[2].Qty.ToString()) : "";

                            for (int i = 0; i < sorts.Count; i++)
                            {
                                if (i <= 2)
                                    otherQty += sorts[i].Qty;
                            }
                            ledShow.Line5 = TxtFormatting("其他工程", (totality - otherQty).ToString());

                            if (isAdd)
                                ledShowBLL.Add(ledShow);
                            else
                                ledShowBLL.Update(ledShow);
                        }
                    }
                }
                else
                {
                    foreach (var item in AreaNames)
                    {
                        var totality = GetCallTotality(item);    //查询区域呼叫总数
                        var list = GetCallList(item);
                        var projectNames = list.Select(x => x.ProjectName).Distinct().ToArray();
                        var otherQty = 0;
                        List<SortModel> sorts = new List<SortModel>();
                        foreach (var name in projectNames)
                        {
                            var count = list.Where(s => s.ProjectName == name).Count();
                            SortModel sortModel = new SortModel();
                            sortModel.ProjectName = name;
                            sortModel.Qty = count;
                            sorts.Add(sortModel);
                        }
                        sorts = sorts.OrderByDescending(a => a.Qty).ToList();
                        LedShowModel ledShow = ledShowBLL.GetModel(item);
                        bool isAdd = false;
                        if (ledShow == null)
                        {
                            ledShow = new LedShowModel();
                            isAdd = true;
                        }
                        ledShow.AreaName = item;
                        ledShow.Line1 = TxtFormatting("呼叫总数", totality.ToString());
                        ledShow.Line2 = sorts.Count >= 1 ? TxtFormatting(sorts[0].ProjectName, sorts[0].Qty.ToString()) : "";
                        ledShow.Line3 = sorts.Count >= 2 ? TxtFormatting(sorts[1].ProjectName, sorts[1].Qty.ToString()) : "";
                        ledShow.Line4 = sorts.Count >= 3 ? TxtFormatting(sorts[2].ProjectName, sorts[2].Qty.ToString()) : "";

                        for (int i = 0; i < sorts.Count; i++)
                        {
                            if (i <= 2)
                                otherQty += sorts[i].Qty;
                        }
                        ledShow.Line5 = TxtFormatting("其他工程", (totality - otherQty).ToString());

                        ledShow.Line1Status = 0;
                        ledShow.Line2Status = 0;
                        ledShow.Line3Status = 0;
                        ledShow.Line4Status = 0;
                        ledShow.Line5Status = 0;

                        if (isAdd)
                            ledShowBLL.Add(ledShow);
                        else
                            ledShowBLL.Update(ledShow);
                    }
                }
                //休眠
                await Task.Delay(GolbalVariable.AppSet.ButtonWorkDispatchTimeSpan);
            }
        }

        /// <summary>
        /// 区域呼叫总数
        /// </summary>
        /// <param name="areaName"></param>
        /// <returns></returns>
        private int GetCallTotality(string areaName)
        {
            DateTime currentTime = DateTime.Now;

            if(currentTime.Hour < 17 && currentTime.Hour >= 7)
            {
                DateTime startTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, 7, 00, 0); // 早上7点
                DateTime endTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, 16, 59, 59); // 下午5点前
                return callDetailsBLL.GetModelCountByWhere(areaName, "", startTime, endTime);
            }else if(currentTime.Hour >= 17)
            {
                DateTime startTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, 17, 0, 0); // 下午5点
                DateTime endTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, 2, 0, 0); 
                endTime = endTime.AddDays(1);                                                                     // 第二天凌晨2点
                return callDetailsBLL.GetModelCountByWhere(areaName, "", startTime, endTime);
            }else
            {
                currentTime = currentTime.AddDays(-1);
                DateTime startTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, 17, 0, 0); // 下午5点
                DateTime endTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, 2, 0, 0);
                endTime = endTime.AddDays(1);                                                                      // 第二天凌晨2点
                return callDetailsBLL.GetModelCountByWhere(areaName, "", startTime, endTime);
            }
        }

        private List<CallDetailsModel> GetCallList(string areaName)
        {
            DateTime currentTime = DateTime.Now;

            if (currentTime.Hour < 17 && currentTime.Hour >= 7)
            {
                DateTime startTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, 7, 00, 0); // 早上7点
                DateTime endTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, 16, 59, 59); // 下午5点前
                return callDetailsBLL.GetModelListBySql(areaName, "", startTime, endTime,1,1000);
            }
            else if (currentTime.Hour >= 17)
            {
                DateTime startTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, 17, 0, 0); // 下午5点
                DateTime endTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, 2, 0, 0);
                endTime = endTime.AddDays(1);                                                                      // 第二天凌晨2点
                return callDetailsBLL.GetModelListBySql(areaName, "", startTime, endTime, 1, 1000);
            }
            else
            {
                currentTime = currentTime.AddDays(-1);
                DateTime startTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, 17, 0, 0); // 下午5点
                DateTime endTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, 2, 0, 0);
                endTime = endTime.AddDays(1);                                                                      // 第二天凌晨2点
                return callDetailsBLL.GetModelListBySql(areaName, "", startTime, endTime, 1, 1000);
            }
        }

        private string TxtFormatting(string txt1, string txt2)
        {
            var leng1 = Encoding.GetEncoding("gb2312").GetBytes(txt1).Length;
            var leng2 = Encoding.GetEncoding("gb2312").GetBytes(txt2).Length;
            if((leng1 + leng2)>= 12)
            {
                var txt = txt1 + txt2;
                return txt;
            }
            else
            {
                var txt = txt1 + new string(' ', 12 - (leng1 + leng2)) + txt2;
                return txt;
            }
        }

        public void CloseAllLed()
        {
            var ledList = GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "LED").ToList();
            foreach (var setting in ledList)
            {
                CloseLed(setting);
            }
        }

        public void CloseLed(SystemDeviceModel model)
        {
            if (model.DeviceIpAddress != null)
            {
                string ret = LEDDisplayDriver.Off(model.DeviceIpAddress);
                string msg = "设置{" + model.DeviceIpAddress + "}关闭->" + ret;
                LogObj.Info(msg);

                if (ret.Contains("失败"))//失败重试5次
                {
                    Thread thread = new Thread(() =>
                    {
                        for (int i = 0; i < 5; i++)
                        {
                            ret = LEDDisplayDriver.Off(model.DeviceIpAddress);
                            msg = "设置{" + model.DeviceIpAddress + "}关闭->" + ret;
                            LogObj.Info(msg);
                            if (ret.Contains("成功"))
                                break;
                        }
                    });
                    thread.IsBackground = true;
                    thread.Start();
                }
            }
        }

        public void OpenAllLed()
        {
            var ledList = GolbalVariable.SystemDeviceBag.Where(x => x.DataSource == "LED").ToList();
            foreach (var setting in ledList)
            {
                OpenLed(setting);
            }
        }

        public void OpenLed(SystemDeviceModel model)
        {
            if (model.DeviceIpAddress != null)
            {
                string ret = LEDDisplayDriver.On(model.DeviceIpAddress);
                string msg = "设置{" + model.DeviceIpAddress + "}打开->" + ret;
                LogObj.Info(msg);
                if (ret.Contains("失败"))//失败重试5次
                {
                    Thread thread = new Thread(() =>
                    {
                        for (int i = 0; i < 5; i++)
                        {
                            ret = LEDDisplayDriver.On(model.DeviceIpAddress);
                            msg = "设置{" + model.DeviceIpAddress + "}打开->" + ret;
                            LogObj.Info(msg);
                            if (ret.Contains("成功"))
                                break;
                        }
                    });
                    thread.IsBackground = true;
                    thread.Start();
                }
            }
        }
    }
}
